package com.ecb.business.service.impl;

import com.ecb.business.dto.api.RemoteResult;
import com.ecb.business.dto.chain.AddChainDTO;
import com.ecb.business.dto.chain.ChainNodeDTO;
import com.ecb.business.dto.chain.HealthCheckDTO;
import com.ecb.business.dto.chain.QueryChainDTO;
import com.ecb.business.exception.BusinessException;
import com.ecb.business.po.ChainConfigPO;
import com.ecb.business.po.ChainNodePO;
import com.ecb.business.po.ChainNodeRelPO;
import com.ecb.business.remote.client.HealthCheckRemote;
import com.ecb.business.repository.ChainConfigDao;
import com.ecb.business.repository.ChainNodeDao;
import com.ecb.business.repository.ChainNodeRelDao;
import com.ecb.business.service.ChainConfigManageService;
import com.ecb.business.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.chainmaker.sdk.config.ChainClientConfig;
import org.chainmaker.sdk.config.NodeConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
@Slf4j
public class ChainConfigManageServiceImpl implements ChainConfigManageService {
    @Autowired
    private ChainConfigDao chainConfigDao;
    @Autowired
    private ChainNodeDao chainNodeDao;
    @Autowired
    private ChainNodeRelDao chainNodeRelDao;
    @Autowired
    private HealthCheckRemote healthCheckRemote;
    @Value("${client.url:127.0.0.1}")
    private String clientUrl;
    @Override
    public List<Map> getChainConfig(QueryChainDTO queryChainDTO){
        List<ChainConfigPO> allByChainIdLikeOrChainNameLikeAndStatusEquals = chainConfigDao.findAllByChainIdLikeOrChainNameLikeAndStatusEquals(queryChainDTO.getChainId(), queryChainDTO.getChainName(), 1);
        List<Map> hashMaps = JsonUtils.beanToArray(allByChainIdLikeOrChainNameLikeAndStatusEquals, Map.class);
        return hashMaps;
    }
    @Override
    public ChainConfigPO getChainConfigById(QueryChainDTO queryChainDTO){
        Optional<ChainConfigPO> byId = chainConfigDao.findById(queryChainDTO.getId());
        if (!byId.isPresent()){
            throw new BusinessException(500, "链不存在");
        }
        return byId.get();
    }

    @Override
    @Transactional
    public boolean addChainConfig(AddChainDTO addChainDTO) {
        List<ChainNodeDTO> nodes = addChainDTO.getChainNodes();
        ChainConfigPO chainConfigPO = JsonUtils.beanToBean(addChainDTO, ChainConfigPO.class);
        // 先进行健康状态检查
        if (!chainHealthCheck(addChainDTO)){
            throw new BusinessException(500, "健康检查失败");
        }

        // todo 重名判断
        // 新增链
        ChainConfigPO config = chainConfigDao.save(chainConfigPO);

        for (ChainNodeDTO node : nodes) {
            ChainNodePO chainNodePO = JsonUtils.beanToBean(node, ChainNodePO.class);
            ChainNodePO save = chainNodeDao.save(chainNodePO);
            ChainNodeRelPO chainNodeRelPO = new ChainNodeRelPO();
            chainNodeRelPO.setChainId(config.getId());
            chainNodeRelPO.setNodeId(save.getId());
            // 添加关联关系
            chainNodeRelDao.save(chainNodeRelPO);
        }
        return true;
    }

    @Override
    public boolean updateChainBaseInfo(AddChainDTO addChainDTO) {
//        Map res = chainConfigDao.findByIdAndStatus(addChainDTO.getId(), 1);
        ChainConfigPO chainConfigPO = JsonUtils.beanToBean(addChainDTO, ChainConfigPO.class);
        chainConfigDao.save(chainConfigPO);
        return true;
    }

    @Override
    public boolean updateChainNode(ChainNodeDTO chainNodeDTO) {
        ChainNodePO chainNodePO = JsonUtils.beanToBean(chainNodeDTO, ChainNodePO.class);
        chainNodeDao.save(chainNodePO);
        return true;
    }

    @Override
    @Transactional
    public boolean deleteChain(AddChainDTO addChainDTO) {
        ChainConfigPO byIdAndStatus = chainConfigDao.findByIdAndStatus(addChainDTO.getId(), 1);
        Map result = JsonUtils.beanToBean(byIdAndStatus, Map.class);
        if (result == null){
            throw new BusinessException(500, "链不存在");
        }
        // 删除链
        ChainConfigPO chainConfigPO = JsonUtils.beanToBean(result, ChainConfigPO.class);
        chainConfigPO.setStatus(9);
        chainConfigDao.save(chainConfigPO);

        //删除节点关联关系
        List<ChainNodeRelPO> allByChainIdAndStatusEquals = chainNodeRelDao.findAllByChainIdAndStatusEquals(chainConfigPO.getId(), 1);
        for (ChainNodeRelPO map : allByChainIdAndStatusEquals) {
            ChainNodeRelPO chainNodeRelPO = JsonUtils.beanToBean(map, ChainNodeRelPO.class);
            chainNodeRelPO.setStatus(9);
            chainNodeRelDao.save(chainNodeRelPO);
            Optional<ChainNodePO> byId = chainNodeDao.findById(chainNodeRelPO.getNodeId());
            if (byId.isPresent()){ // 删除节点
                ChainNodePO chainNodePO = byId.get();
                chainNodePO.setStatus(9);
                chainNodeDao.save(chainNodePO);
            }
        }
        return true;
    }




    private boolean chainHealthCheck(AddChainDTO addChainDTO) {
        HealthCheckDTO healthCheckDTO = new HealthCheckDTO();
        healthCheckDTO.setUserKey(addChainDTO.getUserKey());
        healthCheckDTO.setUserSignKey(addChainDTO.getUserSignKey());
        healthCheckDTO.setUserCrt(addChainDTO.getUserCrt());
        healthCheckDTO.setUserSignCrt(addChainDTO.getUserSignCrt());

        ChainClientConfig chainClientConfig = getChainClientConfig(addChainDTO);
        healthCheckDTO.setChainClientConfig(chainClientConfig);
        String result = healthCheckRemote.healthCheck(clientUrl,healthCheckDTO);
        RemoteResult parse = healthCheckRemote.parse(result);
        if (parse.isOk()){
            return true;
        } else {
            log.info("健康检查失败！");
            return false;
        }
    }

    private static ChainClientConfig getChainClientConfig(AddChainDTO addChainDTO) {
        ChainClientConfig chainClientConfig = new ChainClientConfig();
        chainClientConfig.setChainId(addChainDTO.getChainId());
        chainClientConfig.setChain_id(addChainDTO.getChainId());
        chainClientConfig.setOrgId(addChainDTO.getOrgId());
        chainClientConfig.setOrg_id(addChainDTO.getOrgId());
        chainClientConfig.setUserKeyBytes(addChainDTO.getUserKey().getBytes(StandardCharsets.UTF_8));
        chainClientConfig.setUserCrtBytes(addChainDTO.getUserCrt().getBytes(StandardCharsets.UTF_8));
        chainClientConfig.setUserSignKeyBytes(addChainDTO.getUserSignKey().getBytes(StandardCharsets.UTF_8));
        chainClientConfig.setUserSignCrtBytes(addChainDTO.getUserSignCrt().getBytes(StandardCharsets.UTF_8));
        chainClientConfig.setAuthType(addChainDTO.getAuthType());
        chainClientConfig.setAuth_type(addChainDTO.getAuthType());
        chainClientConfig.setRetryLimit(addChainDTO.getRetryLimit());
        chainClientConfig.setRetry_limit(addChainDTO.getRetryLimit());
        chainClientConfig.setRetryInterval(addChainDTO.getRetryInterval());
        chainClientConfig.setRetry_interval(addChainDTO.getRetryInterval());

        // node
        NodeConfig[] nodes = new NodeConfig[addChainDTO.getChainNodes().size()];
        for (int i = 0; i < addChainDTO.getChainNodes().size(); i++) {
            NodeConfig nodeConfig = new NodeConfig();
            nodeConfig.setConnCnt(addChainDTO.getChainNodes().get(i).getConnCnt());
            nodeConfig.setConn_cnt(addChainDTO.getChainNodes().get(i).getConnCnt());
            nodeConfig.setNodeAddr(addChainDTO.getChainNodes().get(i).getNodeAddr());
            nodeConfig.setNode_addr(addChainDTO.getChainNodes().get(i).getNodeAddr());
            nodeConfig.setTlsHostName(addChainDTO.getChainNodes().get(i).getTlsHostName());
            nodeConfig.setTls_host_name(addChainDTO.getChainNodes().get(i).getTlsHostName());
            nodeConfig.setEnableTls(addChainDTO.getChainNodes().get(i).isEnableTls());
            nodeConfig.setEnable_tls(addChainDTO.getChainNodes().get(i).isEnableTls());
            nodes[i] = nodeConfig;
        }

        chainClientConfig.setNodes(nodes);
        return chainClientConfig;
    }
}
